Bus bus = BusFactory.newInstance().createBus();
All JAX-WS functionalities provided by JBossWS on top of WildFly are currently served through a proper integration of the JBoss Web Services stack with most of the Apache CXF project modules.
Apache CXF is an open source services framework. It allows building and developing services using frontend programming APIs (including JAX-WS), with services speaking a variety of protocols such as SOAP and XML/HTTP over a variety of transports such as HTTP and JMS.
The integration layer (JBossWS-CXF in short hereafter) is mainly meant for:
allowing using standard webservices APIs (including JAX-WS) on WildFly; this is performed internally leveraging Apache CXF without requiring the user to deal with it;
allowing using Apache CXF advanced features (including WS-*) on top of WildFly without requiring the user to deal with / setup / care about the required integration steps for running in such a container.
In order for achieving the goals above, the JBossWS-CXF integration supports the JBoss ws endpoint deployment mechanism and comes with many internal customizations on top of Apache CXF.
In the next sections a list of technical suggestions and notes on the integration is provided; please also refer to the Apache CXF official documentation for in-depth details on the CXF architecture.
The Apache CXF client and endpoint configuration as explained in the Apache CXF official user guide is heavily based on Spring. Apache CXF basically parses Spring cxf.xml descriptors; those may contain any basic bean plus specific ws client and endpoint beans which CXF has custom parsers for. Apache CXF can be used to deploy webservice endpoints on any servlet container by including its libraries in the deployment; in such a scenario Spring basically serves as a convenient configuration option, given direct Apache CXF API usage won't be very handy. Similar reasoning applies on client side, where a Spring based descriptor offers a shortcut for setting up Apache CXF internals.
This said, nowadays almost any Apache CXF functionality can be configured and used through direct API usage, without Spring. As a consequence of that and given the considerations in the sections below, the JBossWS integration with Apache CXF does not rely on Spring descriptors.
WildFly is much more then a servlet container; it actually provides users with a fully compliant target platform for Java EE applications.
Generally speaking, users are encouraged to write portable applications by relying only on JAX-WS specification whenever possible. That would by the way ensure easy migrations to and from other compliant platforms. Being a Java EE container, WildFly already comes with a JAX-WS compliant implementation, which is basically Apache CXF plus the JBossWS-CXF integration layer. So users just need to write their JAX-WS application; no need for embedding any Apache CXF or any ws related dependency library in user deployments. Please refer to the JAX-WS User Guide section of the documentation for getting started.
WS-* usage (including WS-Security, WS-Addressing, WS-ReliableMessaging, ...) should also be configured in the most portable way; that is by relying on proper WS-Policy assertions on the endpoint WSDL contracts, so that client and endpoint configuration is basically a matter of setting few ws context properties. The WS-* related sections of this documentation cover all the details on configuring applications making use of WS-* through policies.
As a consequence of the reasoning above, the JBossWS-CXF integration is currently built directly on the Apache CXF API and aims at allowing users to configure webservice clients and endpoints without Spring descriptors.
Whenever users can't really meet their application requirements with JAX-WS plus WS-Policy, it is of course still possible to rely on direct Apache CXF API usage (given that's included in the AS), loosing the Java EE portability of the application. That could be the case of a user needing specific Apache CXF functionalities, or having to consume WS-* enabled endpoints advertised through legacy wsdl contracts without WS-Policy assertions.
On server side, direct Apache CXF API usage might not be always possible or end up being not very easy. For this reason, the JBossWS integration comes with a convenient alternative through customization options in the jboss-webservices.xml descriptor described below on this page. Properties can be declared in jboss-webservices.xml to control Apache CXF internals like interceptors, features, etc.
Most of the Apache CXF features are configurable using the org.apache.cxf.Bus class. While for basic JAX-WS usage the user might never need to explicitly deal with Bus, using Apache CXF specific features generally requires getting a handle to a org.apache.cxf.Bus instance. This can happen on client side as well as in a ws endpoint or handler business code.
New Bus instances are produced by the currently configured org.apache.cxf.BusFactory implementation the following way:
Bus bus = BusFactory.newInstance().createBus();
The algorithm for selecting the actual implementation of BusFactory to be used leverages the Service API, basically looking for optional configurations in META-INF/services/... location using the current thread context classloader. JBossWS-CXF integration comes with its own implementation of BusFactory, org.jboss.wsf.stack.cxf.client.configuration.JBossWSBusFactory, that allows for seamless setup of JBossWS customizations on top of Apache CXF. So, assuming the JBossWS-CXF libraries are available in the current thread context classloader, the JBossWSBusFactory is automatically retrieved by the BusFactory.newInstance() call above.
JBossWS users willing to explicitly use functionalities of org.apache.cxf.bus.CXFBusFactory, get the same API with JBossWS additions through JBossWSBusFactory:
Map<Class, Object> myExtensions = new HashMap<Class, Object>(); myExtensions.put(...); Bus bus = new JBossWSBusFactory().createBus(myExtensions);
Apache CXF keeps reference to a global default Bus instance as well as to a thread default bus for each thread. That is performed through static members in org.apache.cxf.BusFactory, which also comes with the following methods in the public API:
public static synchronized Bus getDefaultBus() public static synchronized Bus getDefaultBus(boolean createIfNeeded) public static synchronized void setDefaultBus(Bus bus) public static Bus getThreadDefaultBus() public static Bus getThreadDefaultBus(boolean createIfNeeded) public static void setThreadDefaultBus(Bus bus)
Please note that the default behaviour of getDefaultBus() / getDefaultBus(true) / getThreadDefaultBus() / getThreadDefaultBus(true) is to create a new Bus instance if that's not set yet. Moreover getThreadDefaultBus() and getThreadDefaultBus(true) first fallback to retrieving the configured global default bus before actually trying creating a new instance (and the created new instance is set as global default bus if that was not set there yet).
The drawback of this mechanism (which is basically fine in JSE environment) is that when running in WildFly container you need to be careful in order not to (mis)use a bus over multiple applications (assuming the Apache CXF classes are loaded by the same classloader, which is currently the case with WildFly).
Here is a list of general suggestions to avoid problems when running in-container:
forget about the global default bus; you don't need that, so don't do getDefaultBus() / getDefaultBus(true) / setDefaultBus() in your code;
avoid getThreadDefaultBus() / getThreadDefaultBus(true) unless you already know for sure the default bus is already set;
keep in mind thread pooling whenever you customize a thread default bus instance (for instance adding bus scope interceptors, ...), as that thread and bus might be later reused; so either shutdown the bus when you're done or explicitly remove it from the BusFactory thread association.
Finally, remember that each time you explictly create a new Bus instance (factory.createBus()) that is set as thread default bus and global default bus if those are not set yet. The JAXWS Provider implementation also creates Bus instances internally, in particular the JBossWS version of JAXWS Provider makes sure the default bus is never internally used and instead a new Bus is created if required (more details on this in the next paragraph).
JAXWS clients require an Apache CXF Bus to be available; the client is registered within the Bus and the Bus affects the client behavior (e.g. through the configured CXF interceptors). The way a bus is internally selected for serving a given JAXWS client is very important, especially for in-container clients; for this reason, JBossWS users can choose the preferred Bus selection strategy. The strategy is enforced in the javax.xml.ws.spi.Provider implementation from the JBossWS integration, being that called whenever a JAXWS Service (client) is requested.
Each time the vanilla JAXWS api is used to create a Bus, the JBossWS-CXF integration will automatically make sure a Bus is currently associated to the current thread in the BusFactory. If that's not the case, a new Bus is created and linked to the current thread (to prevent the user from relying on the default Bus). The Apache CXF engine will then create the client using the current thread Bus.
This is the default strategy, and the most straightforward one in Java SE environments; it lets users automatically reuse a previously created Bus instance and allows using customized Bus that can possibly be created and associated to the thread before building up a JAXWS client.
The drawback of the strategy is that the link between the Bus instance and the thread needs to be eventually cleaned up (when not needed anymore). This is really evident in a Java EE environment (hence when running in-container), as threads from pools (e.g. serving web requests) are re-used.
When relying on this strategy, the safest approach to be sure of cleaning up the link is to surround the JAXWS client with a try/finally block as below:
try { Service service = Service.create(wsdlURL, serviceQName); MyEndpoint port = service.getPort(MyEndpoint.class); //... } finally { BusFactory.setThreadDefaultBus(null); // OR (if you don't need the bus and the client anymore) Bus bus = BusFactory.getThreadDefaultBus(false); bus.shutdown(true); }
Another strategy is to have the JAXWS Provider from the JBossWS integration create a new Bus each time a JAXWS client is built. The main benefit of this approach is that a fresh bus won't rely on any formerly cached information (e.g. cached WSDL / schemas) which might have changed after the previous client creation. The main drawback is of course worse performance as the Bus creation takes time.
If there's a bus already associated to the current thread before the JAXWS client creation, that is automatically restored when returning control to the user; in other words, the newly created bus will be used only for the created JAXWS client but won't stay associated to the current thread at the end of the process. Similarly, if the thread was not associated to any bus before the client creation, no bus will be associated to the thread at the end of the client creation.
The last strategy is to have the bus created for serving the client be associated to the current thread context classloader (TCCL). That basically means the same Bus instance is shared by JAXWS clients running when the same TCCL is set. This is particularly interesting as each web application deployment usually has its own context classloader, so this strategy is possibly a way to keep the number of created Bus instances bound to the application number in WildFly container.
If there's a bus already associated to the current thread before the JAXWS client creation, that is automatically restored when returning control to the user; in other words, the bus corresponding to the current thread context classloader will be used only for the created JAXWS client but won't stay associated to the current thread at the end of the process. If the thread was not associated to any bus before the client creation, a new bus will be created (and later user for any other client built with this strategy and the same TCCL in place); no bus will be associated to the thread at the end of the client creation.
Users can request a given Bus selection strategy to be used for the client being built by specifying one of the following JBossWS features (which extend javax.xml.ws.WebServiceFeature):
Feature |
Strategy |
org.jboss.wsf.stack.cxf.client.UseThreadBusFeature |
THREAD_BUS |
org.jboss.wsf.stack.cxf.client.UseNewBusFeature |
NEW_BUS |
org.jboss.wsf.stack.cxf.client.UseTCCLBusFeature |
TCCL_BUS |
The feature is specified as follows:
Service service = Service.create(wsdlURL, serviceQName, new UseThreadBusFeature());
If no feature is explicitly specified, the system default strategy is used, which can be modified through the org.jboss.ws.cxf.jaxws-client.bus.strategy system property when starting the JVM. The valid values for the property are THREAD_BUS, NEW_BUS and TCCL_BUS. The default is THREAD_BUS.
The JBossWS-CXF server side integration takes care of internally creating proper Apache CXF structures (including a Bus instance, of course) for the provided ws deployment. Should the deployment include multiple endpoints, those would all live within the same Apache CXF Bus, which would of course be completely separated by the other deployments' bus instances.
While JBossWS sets sensible defaults for most of the Apache CXF configuration options on server side, users might want to fine tune the Bus instance that's created for their deployment; a jboss-webservices.xml descriptor can be used for deployment level customizations.
The jboss-webservices.xml descriptor can be used to provide property values.
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2"> ... <property> <name>...</name> <value>...</value> </property> ... </webservices>
JBossWS-CXF integration comes with a set of allowed property names to control Apache CXF internals.
Apache CXF uses WorkQueue instances for dealing with some operations (e.g. @Oneway requests processing). A WorkQueueManager is installed in the Bus as an extension and allows for adding / removing queues as well as controlling the existing ones.
On server side, queues can be provided by using the cxf.queue.<queue-name>.* properties in jboss-webservices.xml (e.g. cxf.queue.default.maxQueueSize for controlling the max queue size of the default workqueue). At deployment time, the JBossWS integration can add new instances of AutomaticWorkQueueImpl to the currently configured WorkQueueManager; the properties below are used to fill in parameter into the AutomaticWorkQueueImpl constructor:
Property |
Default value |
cxf.queue.<queue-name>.maxQueueSize |
256 |
cxf.queue.<queue-name>.initialThreads |
0 |
cxf.queue.<queue-name>.highWaterMark |
25 |
cxf.queue.<queue-name>.lowWaterMark |
5 |
cxf.queue.<queue-name>.dequeueTimeout |
120000 |
The Apache CXF policy engine supports different strategies to deal with policy alternatives. JBossWS-CXF integration currently defaults to the MaximalAlternativeSelector, but still allows for setting different selector implementation using the cxf.policy.alternativeSelector property in jboss-webservices.xml.
Apache CXF allows managing its MBean objects that are installed into the WildFly MBean server. The feature is enabled on a deployment basis through the cxf.management.enabled property in jboss-webservices.xml. The cxf.management.installResponseTimeInterceptors property can also be used to control installation of CXF response time interceptors, which are added by default when enabling MBean management, but might not be desired in some cases. Here is an example:
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2"> <property> <name>cxf.management.enabled</name> <value>true</value> </property> <property> <name>cxf.management.installResponseTimeInterceptors</name> <value>false</value> </property> </webservices>
Schema validation of exchanged messages can also be enabled in jboss-webservices.xml. Further details available here.
The jboss-webservices.xml descriptor also allows specifying the cxf.interceptors.in and cxf.interceptors.out properties; those allows declaring interceptors to be attached to the Bus instance that's created for serving the deployment.
<?xml version="1.1" encoding="UTF-8"?> <webservices xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee"> <property> <name>cxf.interceptors.in</name> <value>org.jboss.test.ws.jaxws.cxf.interceptors.BusInterceptor</value> </property> <property> <name>cxf.interceptors.out</name> <value>org.jboss.test.ws.jaxws.cxf.interceptors.BusCounterInterceptor</value> </property> </webservices>
The jboss-webservices.xml descriptor also allows specifying the cxf.features property; that allows declaring features to be attached to any endpoint belonging to the Bus instance that's created for serving the deployment.
<?xml version="1.1" encoding="UTF-8"?> <webservices xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee"> <property> <name>cxf.features</name> <value>org.apache.cxf.feature.FastInfosetFeature</value> </property> </webservices>
WS-Discovery support can be turned on in jboss-webservices for the current deployment. Further details available here.
Apache CXF supports declaring interceptors using one of the following approaches:
Annotation usage on endpoint classes (@org.apache.cxf.interceptor.InInterceptor, @org.apache.cxf.interceptor.OutInterceptor)
Direct API usage on client side (through the org.apache.cxf.interceptor.InterceptorProvider interface)
Spring descriptor usage (cxf.xml)
As the Spring descriptor usage is not supported, the JBossWS integration adds an additional descriptor based approach to avoid requiring modifications to the actual client/endpoint code. Users can declare interceptors within predefined client and endpoint configurations by specifying a list of interceptor class names for the cxf.interceptors.in and cxf.interceptors.out properties.
<?xml version="1.0" encoding="UTF-8"?> <jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd"> <endpoint-config> <config-name>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointImpl</config-name> <property> <property-name>cxf.interceptors.in</property-name> <property-value>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointInterceptor,org.jboss.test.ws.jaxws.cxf.interceptors.FooInterceptor</property-value> </property> <property> <property-name>cxf.interceptors.out</property-name> <property-value>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointCounterInterceptor</property-value> </property> </endpoint-config> </jaxws-config>
A new instance of each specified interceptor class will be added to the client or endpoint the configuration is assigned to. The interceptor classes must have a no-argument constructor.
Apache CXF supports declaring features using one of the following approaches:
Annotation usage on endpoint classes (@org.apache.cxf.feature.Features)
Direct API usage on client side (through extensions of the org.apache.cxf.feature.AbstractFeature class)
Spring descriptor usage (cxf.xml)
As the Spring descriptor usage is not supported, the JBossWS integration adds an additional descriptor based approach to avoid requiring modifications to the actual client/endpoint code. Users can declare features within predefined client and endpoint configurations by specifying a list of feature class names for the cxf.features property.
<?xml version="1.0" encoding="UTF-8"?> <jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd"> <endpoint-config> <config-name>Custom FI Config</config-name> <property> <property-name>cxf.features</property-name> <property-value>org.apache.cxf.feature.FastInfosetFeature</property-value> </property> </endpoint-config> </jaxws-config>
A new instance of each specified feature class will be added to the client or endpoint the configuration is assigned to. The feature classes must have a no-argument constructor.
Sections above explain how to declare CXF interceptors and features through properties either in a client/endpoint predefined configuration or in a jboss-webservices.xml descriptor. By getting the feature/interceptor class name only specified, the container simply tries to create a bean instance using the class default constructor. This sets a limitation on the feature/interceptor configuration, unless custom extensions of vanilla CXF classes are provided, with the default constructor setting properties before eventually using the super constructor.
To cope with this issue, JBossWS integration comes with a mechanism for configuring simple bean hierarchies when building them up from properties. Properties can have bean reference values, that is strings starting with ##. Property reference keys are used to specify the bean class name and the value for for each attribute. So for instance the following properties:
Key |
Value |
cxf.features |
##foo, ##bar |
##foo |
org.jboss.Foo |
##foo.par |
34 |
##bar |
org.jboss.Bar |
##bar.color |
blue |
would result into the stack installing two feature instances, the same that would have been created by
import org.Bar; import org.Foo; ... Foo foo = new Foo(); foo.setPar(34); Bar bar = new Bar(); bar.setColor("blue");
The mechanism assumes that the classes are valid beans with proper getter and setter methods; value objects are cast to the correct primitive type by inspecting the class definition. Nested beans can of course be configured.
HTTP transport setup in Apache CXF is achieved through org.apache.cxf.transport.http.HTTPConduit configurations. When running on top of the JBossWS integration, conduits can be programmatically modified using the Apache CXF API as follows:
import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.transport.http.HTTPConduit; import org.apache.cxf.transports.http.configuration.HTTPClientPolicy; //set chunking threshold before using a JAX-WS port client ... HTTPConduit conduit = (HTTPConduit)ClientProxy.getClient(port).getConduit(); HTTPClientPolicy client = conduit.getClient(); client.setChunkingThreshold(8192); ...
Users can also control the default values for the most common HTTPConduit parameters by setting specific system properties; the provided values will override Apache CXF defaut values.
Property |
Description |
cxf.client.allowChunking |
A boolean to tell Apache CXF whether to allow send messages using chunking. |
cxf.client.chunkingThreshold |
An integer value to tell Apache CXF the threshold at which switching from non-chunking to chunking mode. |
cxf.client.connectionTimeout |
A long value to tell Apache CXF how many milliseconds to set the connection timeout to |
cxf.client.receiveTimeout |
A long value to tell Apache CXF how many milliseconds to set the receive timeout to |
cxf.client.connection |
A string to tell Apache CXF to use Keep-Alive or close connection type |
cxf.tls-client.disableCNCheck |
A boolean to tell Apache CXF whether disabling CN host name check or not |
The vanilla Apache CXF defaults apply when the system properties above are not set.